package com.creative.xcx.service.Impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.creative.config.AppConfig;
import com.creative.core.page.TableDataInfo;
import com.creative.core.uitls.Uidutil;
import com.creative.core.uitls.redis.CacheClient;
import com.creative.user.domain.UserInfo;
import com.creative.user.service.UserInfoService;
import com.creative.xcx.domain.Material;
import com.creative.xcx.domain.MaterialType;
import com.creative.xcx.domain.UploadHistory;
import com.creative.xcx.domain.enums.RandomMaterial;
import com.creative.xcx.domain.vo.MaterialVo;
import com.creative.xcx.mapper.MaterialMapper;
import com.creative.xcx.service.MaterialService;
import com.creative.xcx.service.UploadHistoryService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.creative.constant.Constants.CACHE_NULL_TTL;
import static com.creative.constant.Constants.COMMON_PAGE_DATA_TTL;
import static com.creative.xcx.domain.constant.common.CACHE_RANDOM_MATERIAL_KEY;

/**
 * @author ling
 * @description 素材信息
 * @date 2025-03-02
 */
@Service
public class MaterialServiceImpl implements MaterialService {
    public static final Log logger = LogFactory.get();
    @Resource
    private MaterialMapper materialMapper;

    @Resource
    private UserInfoService userInfoService;

    @Resource
    private UploadHistoryService uploadHistoryService;

    @Resource
    private MaterialTypeServiceImpl materialTypeService;

    @Resource
    private CacheClient cacheClient;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private AppConfig appConfig;


    @Override
    public Integer insert(Material material) {
//        判断素材地址是否为空
        if (StrUtil.isBlank(material.getMaterialUrl())) {
            logger.info("添加素材：素材地址为空");
            return 0;
        }

        //        获取用户信息
        UserInfo userInfo = userInfoService.selectById((String) StpUtil.getLoginId());
        //      随机生成uid (15位以内)
        String id = Uidutil.generateUid(15);
        // 设置主键
        material.setMaterialId(id);
        material.setCreateBy(userInfo.getNickName());
        material.setCreateTime(DateUtil.date());
        material.setUpdateTime(DateUtil.date());
        material.setPublishTime(DateUtil.date());
//        如果素材封面为空，则使用默认封面
        if (StrUtil.isBlank(material.getThumbnailUrl())) {
            material.setThumbnailUrl("https://z-mytest.oss-cn-beijing.aliyuncs.com/creative/default-cover.webp");
        }

//        添加上传记录
        UploadHistory uploadHistory = UploadHistory.builder()
                .userId(userInfo.getUserId())
                .materialId(material.getMaterialId())
                .uploadTime(DateUtil.date())
                .build();
        uploadHistoryService.insert(uploadHistory);

//        修改素材分类的更新时间
        MaterialType materialType = MaterialType.builder()
                .typeId(material.getMaterialType())
                .build();
        materialTypeService.update(materialType);

        return materialMapper.insert(material);
    }

    @Override
    public Integer delete(String materialId) {
//        查询素材信息，是否存在该素材
        if (materialMapper.selectById(materialId) == null) {
            return 0;
        }
        this.refreshAllMaterialCaches();
        return materialMapper.delete(materialId);
    }


    @Override
    public Integer deleteBatch(String[] ids) {
        this.refreshAllMaterialCaches();
        return materialMapper.deleteBatch(ids);
    }


    @Override
    public Integer update(Material material) {
        material.setUpdateTime(DateUtil.date());
        material.setUpdateBy(userInfoService.selectById((String) StpUtil.getLoginId()).getNickName());
        Integer result = materialMapper.update(material);
//      素材更新完，在更新缓存
        if (result > 0) {
            // 根据文件后缀刷新缓存
            RandomMaterial randomMaterial = RandomMaterial.getByFileSuffix(material.getMaterialFormatType());
            if (randomMaterial != null) {
                refreshSingleCache(randomMaterial); // 只刷新对应的缓存
            }
        }
        return result;
    }

    @Override
    public Material selectById(String materialId) {
        return materialMapper.selectById(materialId);
    }

    @Override
    public MaterialVo selectAppById(String materialId) {
        return materialMapper.selectAppById(materialId);
    }

    @Override
    public TableDataInfo selectMaterialList(Material material) {
        PageHelper.startPage(material.getPageNum(), material.getPageSize(),"create_time desc");
        List<MaterialVo> list = materialMapper.selectMaterialList(material);
        Page<MaterialVo> page = (Page<MaterialVo>) list;
//    构建返回结果
        TableDataInfo tableDataInfo = new TableDataInfo();
        tableDataInfo.setMsg("查询成功");
        tableDataInfo.setCode(200);
//        设置返回类型结果
        tableDataInfo.setRows(page.getResult());
        tableDataInfo.setTotal(page.getTotal());
        return tableDataInfo;
    }

    @Override
    public List<MaterialVo> getRandomMaterialList(String[] Suffix, String key) {
        return this.getRandomMaterialCache(Suffix, key);
    }


    public List<MaterialVo> getRandomMaterialCache(String[] Suffix, String key) {

        // 1.查询缓存
        String json = stringRedisTemplate.opsForValue().get(key);
        // 处理缓存命中
        if (StrUtil.isNotBlank(json)) {
            logger.info("命中缓存 key: {}", key);
            // 使用安全反序列化方法
            return parseJsonSafely(json, key);
        }

        // 处理空值缓存（防止缓存穿透）
        if (StrUtil.EMPTY.equals(json)) {
            logger.info("命中空值缓存 key: {}", key);
            return Collections.emptyList();
        }
        // 2. 缓存未命中，查询数据库
        List<MaterialVo> list = materialMapper.getRandomMaterialList(Suffix);

        // 3. 处理空结果（防止缓存穿透）
        if (CollUtil.isEmpty(list)) {
            logger.warn("空结果缓存 key: {}", key);
            // 设置短时间空值缓存（5分钟）
            stringRedisTemplate.opsForValue().set(
                    key,
                    StrUtil.EMPTY,
                    CACHE_NULL_TTL,
                    TimeUnit.MINUTES
            );
            return Collections.emptyList();
        }

        // 4. 写入正常缓存
        try {
            cacheClient.set(
                    key,
                    list,  // 直接传递集合对象
                    COMMON_PAGE_DATA_TTL,
                    TimeUnit.HOURS
            );
            logger.info("更新缓存成功 key: {}", key);
        } catch (Exception e) {
            logger.error("缓存写入失败 key: {}", key, e);
            throw new RuntimeException("获取随机素材异常", e);
        }
        return list;
    }

    // 安全的JSON反序列化方法
    private List<MaterialVo> parseJsonSafely(String json, String key) {
        try {
            return JSONUtil.toList(json, MaterialVo.class);
        } catch (Exception e) {
            logger.error("JSON反序列化失败 json: {}", json, e);
            // 清除无效缓存
            stringRedisTemplate.delete(key);
            return Collections.emptyList();
        }
    }

    /**
     * 刷新所有随机素材缓存
     * 全量缓存更新入口
     *
     * @author ling
     * @date 2025/03/18
     */
    public void refreshAllMaterialCaches() {
        RandomMaterial.getAllTypes().parallelStream().forEach(type -> {
            try {
                refreshSingleCache(type);
            } catch (Exception e) {
                logger.error("[{}] 缓存更新异常", type.name(), e);
                throw new RuntimeException("缓存更新异常", e);
            }
        });
    }

    /**
     * 单个类型缓存处理
     *
     * @param randomMaterial 随机材料
     * @author ling
     * @date 2025/03/18
     */
    public void refreshSingleCache(RandomMaterial randomMaterial) {
        String cacheKey = randomMaterial.cacheKey;
        String[] suffixes = randomMaterial.suffixes;

        try {
            // 1. 查询数据库
            List<MaterialVo> materials = materialMapper.getRandomMaterialList(suffixes);
            // 2. 处理空结果
            if (CollUtil.isEmpty(materials)) {
                logger.warn("空结果缓存 key: {}", cacheKey);
                // 设置短时间空值缓存（5分钟）
                stringRedisTemplate.opsForValue().set(
                        cacheKey,
                        StrUtil.EMPTY,
                        CACHE_NULL_TTL,
                        TimeUnit.MINUTES
                );
                return;
            }
            // 3. 写入缓存
            cacheClient.set(
                    cacheKey,
                    materials,  // 直接传递集合对象
                    COMMON_PAGE_DATA_TTL,
                    TimeUnit.HOURS
            );
            logger.info("更新缓存成功 key: {}", cacheKey);

        } catch (Exception e) {
            logger.error("[{}] 缓存处理失败", randomMaterial.name(), e);
            stringRedisTemplate.delete(cacheKey);
            throw new RuntimeException("缓存处理失败", e);
        }
    }

    // 定时任务配置（每日凌晨3点更新）
    @Scheduled(cron = "0 0 17 * * ?")
    public void dailyCacheRefresh() {
        logger.info("=== 开始全量素材缓存更新 ===");
        long start = System.currentTimeMillis();

        refreshAllMaterialCaches();

        logger.info("=== 缓存更新完成，耗时：{}ms ===",
                System.currentTimeMillis() - start);
    }


    @Override
    public List<Material> exportBatch(String[] ids) {
        return materialMapper.exportBatch(ids);
    }

    @Override
    public List<Material> getMaterialKeyword(String keyword) {
        if (keyword != null) {
            keyword = keyword.trim();
        }
        return materialMapper.getMaterialKeyword(keyword);
    }

    @Override
    public TableDataInfo searchMaterialByKeyword(Material material) {
//        去掉空格
        material.setMaterialTags(material.getMaterialTags().trim());
        PageHelper.startPage(material.getPageNum(), material.getPageSize());
        List<MaterialVo> list = materialMapper.searchMaterialByKeyword(material);
        Page<MaterialVo> page = (Page<MaterialVo>) list;
//    构建返回结果
        TableDataInfo tableDataInfo = new TableDataInfo();
        tableDataInfo.setMsg("查询成功");
        tableDataInfo.setCode(200);
        //        转换为vo,使用page.getResult()进行转换
//        设置返回类型结果
        tableDataInfo.setRows(page.getResult());
        tableDataInfo.setTotal(page.getTotal());
        return tableDataInfo;

    }

    @Override
    public List<MaterialVo> getMpMaterialRecommend() {
        String key = CACHE_RANDOM_MATERIAL_KEY;
        String json = stringRedisTemplate.opsForValue().get(CACHE_RANDOM_MATERIAL_KEY);
        // 处理缓存命中
        if (StrUtil.isNotBlank(json)) {
            logger.info("命中缓存 key: {}", key);
            // 使用安全反序列化方法
            return parseJsonSafely(json, key);
        }
        // 处理空值缓存（防止缓存穿透）
        if (StrUtil.EMPTY.equals(json)) {
            logger.info("命中空值缓存 key: {}", key);
            return Collections.emptyList();
        }
        // 2. 缓存未命中，查询数据库
        List<MaterialVo> list = materialMapper.getMpMaterialRecommend();

        // 3. 处理空结果（防止缓存穿透）
        if (CollUtil.isEmpty(list)) {
            logger.warn("空结果缓存 key: {}", key);
            // 设置短时间空值缓存（5分钟）
            stringRedisTemplate.opsForValue().set(
                    key,
                    StrUtil.EMPTY,
                    CACHE_NULL_TTL,
                    TimeUnit.MINUTES
            );
            return Collections.emptyList();
        }

        // 4. 写入正常缓存
        try {
            cacheClient.set(
                    key,
                    list,  // 直接传递集合对象
                    COMMON_PAGE_DATA_TTL,
                    TimeUnit.HOURS
            );
            logger.info("更新缓存成功 key: {}", key);
        } catch (Exception e) {
            logger.error("缓存写入失败 key: {}", key, e);
            throw new RuntimeException("小程序获取随机素材异常", e);
        }
        return list;
    }

    @Override
    public Long calculateMaterial() {
        return materialMapper.calculateMaterial();
    }


    @Override
    public Integer collectMaterial(Material material) {
        material.setUpdateTime(DateUtil.date());
        Integer result = materialMapper.update(material);
//      素材更新完，在更新缓存
//        if (result > 0) {
//            // 根据文件后缀刷新缓存
//            RandomMaterial randomMaterial = RandomMaterial.getByFileSuffix(material.getMaterialFormatType());
//            if (randomMaterial != null) {
//                refreshSingleCache(randomMaterial); // 只刷新对应的缓存
//            }
//        }
        return result;
    }

    @Override
    public Integer unCollectMaterial(Material material) {
        material.setUpdateTime(DateUtil.date());
        Integer result = materialMapper.update(material);
//      素材更新完，在更新缓存
//        if (result > 0) {
//            // 根据文件后缀刷新缓存
//            RandomMaterial randomMaterial = RandomMaterial.getByFileSuffix(material.getMaterialFormatType());
//            if (randomMaterial != null) {
//                refreshSingleCache(randomMaterial); // 只刷新对应的缓存
//            }
//        }
        return result;
    }

}
